home *** CD-ROM | disk | FTP | other *** search
/ Tech Arsenal 1 / Tech Arsenal (Arsenal Computer).ISO / tek-05 / driverss.zip / 8390.ASM < prev    next >
Assembly Source File  |  1991-02-08  |  32KB  |  1,164 lines

  1. ;History:617,1
  2.  
  3. dp8390_version    equ    0    ;version number of the generic 8390 driver.
  4.  
  5. ;  Russell Nelson, Clarkson University.
  6. ;  Copyright, 1988-1991, Russell Nelson
  7. ;  The following people have contributed to this code: David Horne, Eric
  8. ;  Henderson, and Bob Clements.
  9.  
  10. ;   This program is free software; you can redistribute it and/or modify
  11. ;   it under the terms of the GNU General Public License as published by
  12. ;   the Free Software Foundation, version 1.
  13. ;
  14. ;   This program is distributed in the hope that it will be useful,
  15. ;   but WITHOUT ANY WARRANTY; without even the implied warranty of
  16. ;   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  17. ;   GNU General Public License for more details.
  18. ;
  19. ;   You should have received a copy of the GNU General Public License
  20. ;   along with this program; if not, write to the Free Software
  21. ;   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  22.  
  23. ; This driver is the work of several people: Bob Clements, Eric Henderson,
  24. ; Dave Horne, and Russell Nelson.
  25.  
  26.  
  27. sm_rstop_ptr    db    SM_RSTOP_PG
  28.  
  29. rxcr_bits       db      ENRXCR_BCST     ; Default to ours plus multicast
  30.  
  31.  
  32.     public    curr_hw_addr, mcast_list_bits, mcast_all_flag
  33. curr_hw_addr    db    0,0,0,0,0,0    ;Address set into the 8390
  34. mcast_list_bits db      0,0,0,0,0,0,0,0 ;Bit mask from last set_multicast_list
  35. mcast_all_flag  db      0               ;Non-zero if hware should have all
  36.                     ; ones in mask rather than this list.
  37. mcast_sw_filter    db    0        ; set if software filter is required.
  38. mcast_sw_fin    dw    0
  39. mcast_sw_fout    dw    0
  40.  
  41.     public    rcv_modes
  42. rcv_modes    dw    7        ;number of receive modes in our table.
  43.         dw    0               ;There is no mode zero
  44.         dw    rcv_mode_1
  45.         dw    rcv_mode_2
  46.         dw    rcv_mode_3
  47.         dw    rcv_mode_4
  48.         dw    rcv_mode_5
  49.         dw    rcv_mode_6
  50.  
  51.     public    mcast_tab
  52. mcast_hcount    dw    0        ; multicast header count
  53. mcast_tab_b    db    0ffh,0ffh,0ffh,0ffh,0ffh,0ffh ; entry for broadcast
  54. mcast_tab    db    (MAX_MULTICAST*EADDR_LEN) dup (0)
  55. ;
  56. ;    a temp buffer for the received header
  57. ;
  58. RCV_HDR_SIZE    equ    26        ; 2 ids @6 + protocol @2+8, + header @4
  59. rcv_hdr        db    RCV_HDR_SIZE dup(0)
  60.  
  61. ;
  62. ;    The board data
  63. ;
  64.         public    board_data
  65. BOARD_DATA_SIZE equ    32
  66. board_data    db     BOARD_DATA_SIZE dup(0)
  67. soft_tx_errors        dw    0,0
  68. soft_tx_err_bits    db    0
  69. soft_rx_errors        dw    0,0
  70. soft_rx_err_bits    db    0
  71.  
  72.  
  73.  
  74. ifdef    debug            ; Include a very useful logging mechanism.  
  75.  
  76. ; The log entry structure.  Log entries include useful data such as
  77. ; a type (each place a log entry is made uses a different type), various
  78. ; chip status, ring buffer status, log entry dependent data, and optionally
  79. ; 8259 interrupt controller status.
  80. logentry    struc
  81. le_type        db    0    ; Log entry type
  82. le_ccmd        db    ?    ; Value of CCMD register
  83. le_isr        db    ?    ; Value of ISR register
  84. le_tsr        db    ?    ; Value of TSR register
  85. le_tcur        dw    ?    ; Value of sm_tcur
  86. le_tboundary    dw    ?    ; Value of sm_tboundary
  87. le_tnum        dw    ?    ; Value of sm_tnum
  88. le_dw        dw    ?    ; Log type specific dw data
  89. ifndef    mkle8259        ; Log 8259 status?
  90. le_dd        dd    ?    ; Log type specific dd data
  91. else
  92. le_irr1        db    ?    ; Value of 8259-1 IRR register
  93. le_isr1        db    ?    ; Value of 8259-1 ISR register
  94. le_irr2        db    ?    ; Value of 8259-2 IRR register
  95. le_isr2        db    ?    ; Value of 8259-2 ISR register
  96. endif
  97. logentry    ends
  98.  
  99. ; The types of log entries.
  100. LE_SP_E        equ    0    ; send_pkt entry
  101. LE_SP_X        equ    1    ; send_pkt exit
  102. LE_ASP_E    equ    2    ; as_send_pkt entry
  103. LE_ASP_X    equ    3    ; as_send_pkt exit
  104. LE_RBALLOC_E    equ    4    ; tx_rballoc entry
  105. LE_RBALLOC_X    equ    5    ; tx_rballoc exit
  106. LE_COPY_E    equ    6    ; sm_copy entry
  107. LE_COPY_X    equ    7    ; sm_copy exit
  108. LE_START_E    equ    8    ; tx_start entry
  109. LE_START_X    equ    9    ; tx_start exit
  110. LE_XMIT_E    equ    0ah    ; xmit entry
  111. LE_XMIT_X    equ    0bh    ; xmit exit
  112. LE_TXISR_E    equ    0ch    ; txisr entry
  113. LE_TXISR_X    equ    0dh    ; txisr exit
  114. LE_RECV_E    equ    0eh    ; recv entry
  115. LE_RECV_X    equ    0fh    ; recv exit
  116. LE_RCVFRM_E    equ    10h    ; rcv_frm entry
  117. LE_RCVFRM_X    equ    11h    ; rcv_frm exit
  118. LE_COPY_L    equ    12h    ; sm_copy loop
  119. LE_TIMER_E    equ    13h    ; timer entry
  120. LE_TIMER_X    equ    14h    ; timer exit
  121.  
  122.     public    log, log_index
  123. log        logentry 256 dup (<>) ; The log itself
  124. log_index    db    0    ; Index to current log entry
  125.  
  126. ; The macro used to create log entries.
  127. mkle    macro    letype, ledw, ledd, ledd2 ; Make an entry in the log
  128.     pushf            ; Save interrupt enable state
  129.     cli            ; Disable interrupts
  130.     push    dx        ; Save registers
  131.     push    bx
  132.     push    ax
  133.     mov bl,    log_index    ; Get current log_index
  134.     xor bh,    bh        ; Clear high byte
  135.     shl bx,    1        ; Multiply by sixteen
  136.     shl bx,    1
  137.     shl bx,    1
  138.     shl bx,    1
  139.     mov log[bx].le_type, letype ; Store log entry type
  140.     loadport        ; Base of device
  141.     setport EN_CCMD    ; Point at chip command register
  142.     in al,    dx        ; Get chip command state
  143.     mov log[bx].le_ccmd, al    ; Store CCMD value
  144.     setport EN0_ISR        ; Point at chip command register
  145.     in al,    dx        ; Get chip command state
  146.     mov log[bx].le_isr, al    ; Store ISR value
  147.     setport EN0_TSR        ; Point at chip command register
  148.     in al,    dx        ; Get chip command state
  149.     mov log[bx].le_tsr, al    ; Store TSR value
  150.     mov ax,    sm_tcur        ; Get current sm_tcur
  151.     mov log[bx].le_tcur, ax    ; Store sm_tcur value
  152.     mov ax,    sm_tboundary    ; Get current sm_tboundary
  153.     mov log[bx].le_tboundary, ax ; Store sm_tboundary value
  154.     mov ax,    sm_tnum        ; Get current sm_tnum
  155.     mov log[bx].le_tnum, ax    ; Store sm_tnum value
  156.     mov log[bx].le_dw, ledw    ; Store log entry dw
  157. ifndef    mkle8259        ; Include extra per-type data
  158.     mov word ptr log[bx].le_dd, ledd ; Store low word of log entry dd
  159.     mov word ptr log[bx].le_dd+2, ledd2 ; Store high word of log entry dd
  160. else                ; Include 8259 status
  161.     mov    al,0ah        ; read request register from
  162.     out    0a0h,al        ; secondary 8259
  163.     nop            ; settling delay
  164.     nop
  165.     nop
  166.     in    al,0a0h        ; get it
  167.     mov log[bx].le_irr2, al
  168.     mov    al,0bh        ; read in-service register from
  169.     out    0a0h,al        ; secondary 8259
  170.     nop            ; settling delay
  171.     nop
  172.     nop
  173.     in    al,0a0h        ; get it
  174.     mov log[bx].le_isr2, al
  175.     mov    al,0ah        ; read request register from
  176.     out    020h,al        ; primary 8259
  177.     nop            ; settling delay
  178.     nop
  179.     nop
  180.     in    al,020h        ; get it
  181.     mov log[bx].le_irr1, al
  182.     mov    al,0bh        ; read in-service register from
  183.     out    020h,al        ; primary 8259
  184.     nop            ; settling delay
  185.     nop
  186.     nop
  187.     in    al,020h        ; get it
  188.     mov log[bx].le_isr1, al
  189. endif
  190. ifdef    screenlog        ; Log the entry type to the screen too
  191.     push    es
  192.     mov ax,    0b800h        ; Color screen only...
  193.     mov es,    ax
  194.     mov bl,    log_index    ; Get current log_index
  195.     xor bh,    bh        ; Clear high byte
  196.     shl bx,    1        ; Multiply by sixteen
  197.     add bx,    3360
  198.     mov byte ptr es:[bx-1], 07h
  199.     mov byte ptr es:[bx], letype+30h
  200.     mov byte ptr es:[bx+1], 70h
  201.     pop    es
  202. endif
  203.     inc    log_index    ;
  204.     pop    ax        ; Restore registers
  205.     pop    bx
  206.     pop    dx
  207.     popf            ; Restore interrupt enable state
  208.     endm
  209.  
  210. else
  211. mkle    macro    letype, ledw, ledd, ledd2 ; Define an empty macro
  212.     endm
  213. endif
  214.  
  215.     public    as_send_pkt
  216. ; The Asynchronous Transmit Packet routine.
  217. ; Enter with es:di -> i/o control block, ds:si -> packet, cx = packet length,
  218. ;   interrupts possibly enabled.
  219. ; Exit with nc if ok, or else cy if error, dh set to error number.
  220. ;   es:di and interrupt enable flag preserved on exit.
  221. as_send_pkt:
  222.     ret
  223.  
  224.     public    drop_pkt
  225. ; Drop a packet from the queue.
  226. ; Enter with es:di -> iocb.
  227. drop_pkt:
  228.     assume    ds:nothing
  229.     ret
  230.  
  231.     public    xmit
  232. ; Process a transmit interrupt with the least possible latency to achieve
  233. ;   back-to-back packet transmissions.
  234. ; May only use ax and dx.
  235. xmit:
  236.     assume    ds:nothing
  237.     ret
  238.  
  239.  
  240.     public    send_pkt
  241. send_pkt:
  242. ;enter with ds:si -> packet, cx = packet length.
  243. ;exit with nc if ok, or else cy if error, dh set to error number.
  244.     assume    ds:nothing
  245.     mkle LE_SP_E, cx, si, ds
  246. ;ne1000 checks the packet size at this point, which is probably more sensible.
  247.     loadport        ; Point at chip command register
  248.     setport EN_CCMD        ; ..
  249.     pause_
  250. ;ne1000 fails to check to see if the transmitter is still busy.
  251.     mov bx,    8000h        ; Avoid infinite loop
  252. tx_wait:
  253.     in al,    dx        ; Get chip command state
  254.     test al,ENC_TRANS    ; Is transmitter still running?
  255.     jz    tx_idle        ; Go if free
  256.     dec    bx        ; Count the timeout
  257.     jnz    tx_wait        ; Fall thru if TX is stuck
  258.     call    count_out_err    ; Should count these error timeouts
  259.                 ; Maybe need to add recovery logic here
  260. tx_idle:
  261.     cmp    cx,GIANT    ; Is this packet too large?
  262.     ja    send_pkt_toobig
  263.  
  264.     cmp cx,    RUNT        ; Is the frame long enough?
  265.     jnb    tx_oklen    ; Go if OK
  266.     mov cx,    RUNT        ; Stretch frame to minimum allowed
  267. tx_oklen:
  268.     push    cx        ; Hold count for later
  269.     loadport        ; Set up for address
  270.     setport EN0_ISR
  271.     pause_
  272.     mov    al,ENISR_RDC    ; clear remote interrupt int.
  273.     out    dx,al
  274.     setport    EN0_TCNTLO    ; Low byte of TX count
  275.     pause_
  276.     mov al,    cl        ; Get the count
  277.     out dx,    al        ; Tell card the count
  278.     setport    EN0_TCNTHI    ; High byte of TX count
  279.     pause_
  280.     mov al,    ch        ; Get the count
  281.     out dx,    al        ; Tell card the count
  282.     xor ax,    ax        ; Set up ax at base of tx buffer
  283.     mov ah,    SM_TSTART_PG    ; Where to put tx frame
  284.     pop    cx        ; Get back count to give to board
  285.     call    block_output
  286.     jc    tx_no_rdc
  287.     loadport
  288.     setport    EN0_TPSR    ; Transmit Page Start Register
  289.     pause_
  290.     mov al,    SM_TSTART_PG
  291.     out dx,    al        ; Start the transmitter
  292.     setport    EN_CCMD        ; Chip command reg
  293.     pause_
  294.     mov al,    ENC_TRANS+ENC_NODMA+ENC_START
  295.     out dx,    al        ; Start the transmitter
  296.     mkle LE_SP_X, cx, 1, 0
  297.     clc            ; Successfully started
  298.     sti
  299.     ret            ; End of transmit-start routine
  300. send_pkt_toobig:
  301.     mov    dh,NO_SPACE
  302.     stc
  303.     sti
  304.     ret
  305. tx_no_rdc:
  306.     mov    dh,CANT_SEND
  307.     mkle LE_SP_X, cx, 0, 0
  308.     stc
  309.     sti
  310.     ret
  311.  
  312. count_soft_err:
  313.     add    word ptr soft_tx_errors,1
  314.     adc    word ptr soft_tx_errors+2,0
  315.     or    byte ptr soft_tx_err_bits,al
  316.     ret
  317.  
  318.  
  319.     public    get_address
  320. get_address:
  321. ;get the address of the interface.
  322. ;enter with es:di -> place to get the address, cx = size of address buffer.
  323. ;exit with nc, cx = actual size of address, or cy if buffer not big enough.
  324. ; Give caller the one currently in the 8390, not necessarily the one in PROM.
  325.     assume ds:code
  326.     cmp cx,    EADDR_LEN    ; Caller wants a reasonable length?
  327.     jb    get_addr_x    ; No, fail.
  328.     mov cx,    EADDR_LEN    ; Move one ethernet address from our copy
  329.     mov si, offset curr_hw_addr     ; Copy from most recent setting
  330.     rep     movsb
  331.     mov cx,    EADDR_LEN    ; Tell caller how many bytes we fed him
  332.     clc            ; Carry off says success
  333.     ret
  334. get_addr_x:
  335.     stc            ; Tell caller our addr is too big for him
  336.     ret
  337.  
  338.  
  339.     public    set_address
  340. set_address:
  341.     assume    ds:nothing
  342. ;enter with ds:si -> Ethernet address, CX = length of address.
  343. ;exit with nc if okay, or cy, dh=error if any errors.
  344. ;
  345.     cmp    cx,EADDR_LEN        ;ensure that their address is okay.
  346.     je    set_address_4
  347.     mov    dh,BAD_ADDRESS
  348.     stc
  349.     jmp    short set_address_done
  350. set_address_4:
  351.     push    cs              ; Copy from them to our RAM copy
  352.     pop     es              ; Destination of move
  353.     mov di, offset curr_hw_addr
  354.     rep     movsb           ; Move their address
  355.     call    set_8390_eaddr  ; Put that address in the chip
  356. set_address_okay:
  357.     mov    cx,EADDR_LEN        ;return their address length.
  358.     clc
  359. set_address_done:
  360.     push    cs
  361.     pop    ds
  362.     assume    ds:code
  363.     ret
  364.  
  365. ; Copy our Ethernet address from curr_hw_addr into the DS8390
  366. set_8390_eaddr:
  367.     cld
  368.     push    cs              ; Get it from our local RAM copy
  369.     pop     ds
  370.     mov si, offset curr_hw_addr
  371.     mov cx,    EADDR_LEN    ; Move one ethernet address from our copy
  372.     loadport
  373.     setport    EN_CCMD        ; Chip command register
  374.     pause_
  375.     cli            ; Protect from irq changing page bits
  376.     mov al,    ENC_NODMA+ENC_PAGE1    ;+ENC_STOP
  377.     out dx,    al        ; Switch to page one for writing eaddr
  378.     setport    EN1_PHYS    ; Where it goes in 8390
  379. set_8390_1:
  380.     lodsb
  381.     out    dx,al
  382.     pause_
  383.     inc    dx
  384.     loop    set_8390_1
  385.     loadport
  386.     setport    EN_CCMD        ; Chip command register
  387.     pause_
  388.     mov al,    ENC_NODMA+ENC_PAGE0    ;+ENC_STOP
  389.     out dx,    al        ; Restore to page zero
  390.     sti            ; OK for interrupts now
  391.     ret
  392.  
  393. ; Routines to set address filtering modes in the DS8390
  394. rcv_mode_1:     ; Turn off receiver
  395.     mov al,    ENRXCR_MON      ; Set to monitor for counts but accept none
  396.     jmp short rcv_mode_set
  397. rcv_mode_2:     ; Receive only packets to this interface
  398.     mov al, 0               ; Set for only our packets
  399.     jmp short rcv_mode_set
  400. rcv_mode_3:     ; Mode 2 plus broadcast packets (This is the default)
  401.     mov al,    ENRXCR_BCST     ; Set four ours plus broadcasts
  402.     jmp short rcv_mode_set
  403. rcv_mode_4:     ; Mode 3 plus selected multicast packets
  404.     mov al,    ENRXCR_BCST+ENRXCR_MULTI ; Ours, bcst, and filtered multicasts
  405.     mov     mcast_all_flag,0    ; need to do sw filter.
  406.     mov    mcast_sw_filter,1    ; because chip filter is not 100%
  407.     jmp short rcv_mode_set
  408. rcv_mode_5:     ; Mode 3 plus ALL multicast packets
  409.     mov al,    ENRXCR_BCST+ENRXCR_MULTI ; Ours, bcst, and filtered multicasts
  410.     mov     mcast_all_flag,1
  411.     jmp short rcv_mode_set
  412. rcv_mode_6:     ; Receive all packets (Promiscuous physical plus all multi)
  413.     mov al,    ENRXCR_BCST+ENRXCR_MULTI+ENRXCR_PROMP
  414.     mov     mcast_all_flag,1
  415. rcv_mode_set:
  416.     push    ax              ; Hold mode until masks are right
  417.     call    set_8390_multi  ; Set the multicast mask bits in chip
  418.     pop     ax
  419.     loadport
  420.     setport    EN0_RXCR    ; Set receiver to selected mode
  421.     pause_
  422.     out dx,    al
  423.     mov     rxcr_bits,al    ; Save a copy of what we set it to
  424.     ret
  425.  
  426.  
  427.     public    set_multicast_list
  428. set_multicast_list:
  429. ;enter with ds:si ->list of multicast addresses, cx = number of addresses.
  430. ;return nc if we set all of them, or cy,dh=error if we didn't.
  431.     assume ds:nothing
  432.     push    cs
  433.     pop    es        ; set es to destination
  434.     mov    di,offset mcast_tab
  435.     mov    ax,cx        ; save byte count
  436.     repz    movsb
  437.     mov    dx,0
  438.     mov    cx,6
  439.     div    cx
  440.     mov    mcast_hcount,ax
  441. ;
  442.     mov    word ptr mcast_list_bits,0
  443.     mov    word ptr mcast_list_bits+2,0
  444.     mov    word ptr mcast_list_bits+4,0
  445.     mov    word ptr mcast_list_bits+6,0
  446. ;
  447.     mov    cx,mcast_hcount
  448.     inc    cx
  449.     mov    di,offset mcast_tab_b
  450. set_mcl_1:
  451.     call    add_mc_bits
  452.     add    di,6
  453.     loop    set_mcl_1
  454.     call    set_8390_multi  ; Set the multicast mask bits in chip
  455.     clc
  456.     mov    dh,0
  457.     ret
  458.  
  459. ;
  460. ;    multicast is at es:di
  461.     assume    ds:nothing
  462. add_mc_bits:
  463.     push    cx
  464.     push    di
  465.     mov    cx,6
  466.     mov    dx,0ffffh            ; this is msw.
  467.     mov    bx,0ffffh            ; set 32 bit number
  468. add_mcb_1:
  469.     mov    al,es:[di]
  470.     inc    di
  471.     call    upd_crc            ; update crc
  472.     loop    add_mcb_1        ; and loop.
  473.     mov    ah,0
  474.     mov    al,dh            ; get ms 8 bits,
  475.     rol    al,1
  476.     rol    al,1
  477.     rol    al,1            ; put 3 bits at bottom
  478.     and    al,7
  479.     mov    dl,al            ; save in dl
  480.     mov    al,dh            ; get ms 8 bits,
  481.     ror    al,1
  482.     ror    al,1            ; but at bottom
  483.     and    al,7
  484.     mov    cl,al            ; save in cl
  485.     mov    al,1
  486.     rol    al,cl            ; set the correct bit,
  487.     mov    di,offset mcast_list_bits
  488.     mov    dh,0
  489.     add    di,dx
  490.     or    cs:[di],al
  491.     pop    di
  492.     pop    cx
  493.     ret
  494.  
  495. ;
  496. ;    dx is high,
  497. ;    bx is low.
  498. ;    al is data
  499.  
  500. upd_crc:
  501.     push    cx
  502.     mov    cx,8        ; do 8 bits
  503.     mov    ah,0
  504. upd_crc1:
  505.     shl    bx,1        ; shift bx
  506.     rcl    dx,1        ; through dx
  507.     rcl    ah,1        ; carry is at bottom of ah
  508.     xor    ah,al        ; xor with lsb of data
  509.     rcr    ah,1        ; and put in carry bit
  510.     jnc    upd_crc2
  511. ;
  512. ;    autodin is x^32+x^26+x^23x^22+x^16+
  513. ;    x^12+x^11+x^10+x^8+x^7+x^5+x^4+x^2+x^1+1
  514.  
  515.     xor    dx,0000010011000001b
  516.     xor    bx,0001110110110111b
  517. upd_crc2:
  518.     shr    al,1        ; shift the data
  519.     loop    upd_crc1
  520.     pop    cx
  521.     ret
  522.  
  523. ; Set the multicast filter mask bits in case promiscuous rcv wanted
  524. set_8390_multi:
  525.     push    cs
  526.     pop     ds
  527.     assume    ds:code
  528.     loadport
  529.     setport    EN_CCMD        ; Chip command register
  530.     pause_
  531.     mov cx,    8        ; Eight bytes of multicast filter
  532.     mov si, offset mcast_list_bits  ; Where bits are, if not all ones
  533.     cli            ; Protect from irq changing page bits
  534.     mov al,    ENC_NODMA+ENC_PAGE1+ENC_STOP
  535.     out dx,    al        ; Switch to page one for writing eaddr
  536.     setport    EN1_MULT    ; Where it goes in 8390
  537.     pause_
  538.     mov al, mcast_all_flag  ; Want all ones or just selected bits?
  539.     or al,  al
  540.     je      set_mcast_2     ; Just selected ones
  541.     mov al,    0ffh        ; Ones for filter
  542. set_mcast_all:
  543.     out dx,    al        ; Write a mask byte
  544.     inc    dl        ; Step to next one
  545.     loop    set_mcast_all    ; ..
  546.     jmp short set_mcast_x
  547.  
  548. set_mcast_2:
  549.     lodsb                   ; Get a byte of mask bits
  550.     out dx,    al        ; Write a mask byte
  551.     inc    dl        ; Step to next I/O register
  552.     loop    set_mcast_2     ; ..
  553. set_mcast_x:
  554.     loadport
  555.     setport    EN_CCMD        ; Chip command register
  556.     pause_
  557.     mov al,    ENC_NODMA+ENC_PAGE0+ENC_START
  558.     out dx,    al        ; Restore to page zero
  559.     sti            ; OK for interrupts now
  560.     ret
  561.  
  562.  
  563.     public    reset_board
  564. reset_board:
  565.     assume ds:nothing
  566.     reset_8390
  567.     setport    EN_CCMD        ; Chip command reg
  568.     pause_
  569.     mov al,    ENC_STOP+ENC_NODMA
  570.     out dx,    al        ; Stop the DS8390
  571.     setport EN0_ISR
  572.     pause_
  573.     mov    ax,18            ;half a second ought be to enuf.
  574.     call    set_timeout
  575. reset_board_loop:
  576.     in    al,dx        ; get isr
  577.     and    al,ENISR_RESET
  578.     jnz    reset_board_done
  579.     call    do_timeout
  580.     jne    reset_board_loop
  581. reset_board_done:
  582.     ret
  583.  
  584.     public    terminate
  585. terminate:
  586.     terminate_board
  587.     ret
  588.  
  589.     public    reset_interface
  590. reset_interface:
  591.     assume ds:code
  592.     call    reset_board
  593.     loadport        ; Base of I/O regs
  594.     setport    EN0_ISR        ; Interrupt status reg
  595.     pause_
  596.     mov al,    0ffh        ; Clear all pending interrupts
  597.     out dx,    al        ; ..
  598.     setport    EN0_IMR        ; Interrupt mask reg
  599.     pause_
  600.     xor al,    al        ; Turn off all enables
  601.     out dx,    al        ; ..
  602.     ret
  603.  
  604. ; Linkages to non-device-specific routines
  605. ;called when we want to determine what to do with a received packet.
  606. ;enter with cx = packet length, es:di -> packet type, dl = packet class.
  607. ;It returns with es:di = 0 if don't want this type or if no buffer available.
  608.     extrn    recv_find: near
  609.  
  610. ;called after we have copied the packet into the buffer.
  611. ;enter with ds:si ->the packet, cx = length of the packet.
  612.     extrn    recv_copy: near
  613.  
  614.     extrn    count_in_err: near
  615.     extrn    count_out_err: near
  616.  
  617.     public    recv
  618. recv:
  619. ;called from the recv isr.  All registers have been saved, and ds=cs.
  620. ;Actually, not just receive, but all interrupts come here.
  621. ;Upon exit, the interrupt will be acknowledged.
  622. ;ne1000 and ne2000 routines are identical to this point (except that ne2000
  623. ;  masks off interrupts).
  624.     assume    ds:code
  625.     mkle LE_RECV_E, 0, 0, 0
  626.  
  627. check_isr:            ; Was there an interrupt from this card?
  628.     loadport        ; Point at card's I/O port base
  629.     ram_enable
  630.     setport EN0_IMR        ; point at interrupt masks
  631.     pause_            ; switch off, this way we can
  632.     mov    al,0        ; leave the chip running.
  633.     out    dx,al        ; no interrupts please.
  634.     setport    EN0_ISR        ; Point at interrupt status register
  635.     pause_
  636.     in al,    dx        ; Get pending interrupts
  637.     and al,    ENISR_ALL    ; Any?
  638.     jnz    isr_test_overrun
  639.     mkle LE_RECV_X, 0, 0, 0
  640.     jmp    interrupt_done    ; Go if none
  641. ; First, a messy procedure for handling the case where the rcvr
  642. ; over-runs its ring buffer.  This is spec'ed by National for the chip.
  643. ; This is handled differently in sample code from 3Com and from WD.
  644. ; This is close to the WD version.  May need tweaking if it doesn't
  645. ; work for the 3Com card.
  646.  
  647. isr_test_overrun: 
  648.     test al,ENISR_OVER    ; Was there an overrun?
  649.     jnz    recv_overrun    ; Go if so.
  650.     jmp    recv_no_overrun    ; Go if not.
  651. recv_overrun:
  652.     setport    EN_CCMD        ; Stop the chip
  653.     pause_
  654.     mov al,    ENC_STOP+ENC_NODMA
  655.     out dx,    al        ; Write "stop" to command register
  656.  
  657.     mov al, ENC_NODMA+ENC_PAGE1    ; Could be in previous out, but
  658.     out dx,al        ; was only tested this way
  659.     setport EN1_CURPAG    ; Get current page
  660.     in al,dx
  661.     mov bl,al        ; save it
  662.     setport    EN_CCMD        ;
  663.     mov al, ENC_NODMA+ENC_PAGE0
  664.     out dx,al        ; Back to page 0
  665.  
  666. ; Remove one frame from the ring
  667.     setport    EN0_BOUNDARY    ; Find end of this frame
  668.     pause_
  669.     in al,    dx        ; Get memory page number
  670.     inc    al        ; Page plus 1
  671.     cmp al,    sm_rstop_ptr    ; Wrapped around ring?
  672.     jnz    rcv_ovr_nwrap    ; Go if not
  673.     mov al,    SM_RSTART_PG    ; Yes, wrap the page pointer
  674. rcv_ovr_nwrap:
  675.  
  676.     cmp    al,bl        ; Check if buffer emptry
  677.     je    rcv_ovr_empty    ; Yes ? Don't receive anything
  678.  
  679. ;ne1000 and ne2000 routines are identical to this point (except that ne2000
  680. ;  masks off interrupts).
  681.     mov    ah,al        ; make a byte address. e.g. page
  682.     mov    bl,ah        ; and save in bl
  683.     mov    al,0        ; 46h becomes 4600h into buffer
  684.     mov    cx,RCV_HDR_SIZE    ; size of rcv_hdr
  685.     mov    di,offset rcv_hdr ;point to header
  686.     push    ds
  687.     pop    es        ; set es to right place
  688.     call    block_input
  689.     mov al,    rcv_hdr+EN_RBUF_STAT    ; Get the buffer status byte
  690.     test al,ENRSR_RXOK    ; Is this frame any good?
  691.     jz    rcv_ovr_ng    ; Skip if not
  692.      call    rcv_frm        ; Yes, go accept it
  693. rcv_ovr_ng:
  694.     mov    al,rcv_hdr+EN_RBUF_NXT_PG ; Get pointer to next frame
  695.     dec    al        ; Back up one page
  696.     cmp    al,SM_RSTART_PG    ; Did it wrap?
  697.     jae    rcv_ovr_nwr2
  698.     mov    al,sm_rstop_ptr    ; Yes, back to end of ring
  699.     dec    al
  700. rcv_ovr_nwr2:
  701.     loadport        ; Point at boundary reg
  702.     setport    EN0_BOUNDARY    ; ..
  703.     pause_
  704.     out dx,    al        ; Set the boundary
  705. rcv_ovr_empty:
  706.     loadport        ; Point at boundary reg
  707.     setport    EN0_RCNTLO    ; Point at byte count regs
  708.     pause_
  709.     xor al,    al        ; Clear them
  710.     out dx,    al        ; ..
  711.     setport    EN0_RCNTHI
  712.     pause_
  713.     out dx,    al
  714.     setport    EN0_ISR        ; Point at status reg
  715.     pause_
  716.     mov cx,    8000h        ; Timeout counter
  717. rcv_ovr_rst_loop:
  718.     in al,    dx        ; Is it finished resetting?
  719.     test al,ENISR_RESET    ; ..
  720.     jmp    $+2        ; limit chip access rate
  721.     loopnz    rcv_ovr_rst_loop; Loop til reset, or til timeout
  722.     loadport        ; Point at Transmit control reg
  723.      setport    EN0_TXCR    ; ..
  724.     pause_
  725.     mov al,    ENTXCR_LOOP    ; Put transmitter in loopback mode
  726.     out dx,    al        ; ..
  727.     setport    EN_CCMD        ; Point at Chip command reg
  728.     pause_
  729.     mov al,    ENC_START+ENC_NODMA
  730.     out dx,    al        ; Start the chip running again
  731.     setport    EN0_TXCR    ; Back to TX control reg
  732.     pause_
  733.     xor al,    al        ; Clear the loopback bit
  734.     out dx,    al        ; ..
  735.     setport    EN0_ISR        ; Point at Interrupt status register
  736.     pause_
  737.     mov al,    ENISR_OVER    ; Clear the overrun interrupt bit
  738.     out dx,    al        ; ..
  739.     call    count_in_err    ; Count the anomaly
  740.      jmp    check_isr    ; Done with the overrun case
  741.  
  742. recv_no_overrun:
  743. ; Handle receive flags, normal and with error (but not overrun).
  744.     test al,ENISR_RX+ENISR_RX_ERR    ; Frame received without overrun?
  745.     jnz    recv_frame    ; Go if so.
  746.     jmp    recv_no_frame    ; Go if not.
  747. recv_frame:
  748.     loadport        ; Point at Chip's Command Reg
  749.     setport    EN0_ISR        ; Point at Interrupt status register
  750.     pause_
  751.     mov al,    ENISR_RX+ENISR_RX_ERR
  752.     out dx,    al        ; Clear those requests
  753.      setport    EN_CCMD        ; ..
  754.     pause_
  755.     mov al,    ENC_NODMA+ENC_PAGE1+ENC_START
  756.     out dx,    al        ; Switch to page 1 registers
  757.     setport    EN1_CURPAG    ;Get current page of rcv ring
  758.     pause_
  759.     in al,    dx        ; ..
  760.     mov ah,    al        ; Hold current page in AH
  761.      setport    EN_CCMD        ; Back to page zero registers
  762.     pause_
  763.     mov al,    ENC_NODMA+ENC_PAGE0+ENC_START
  764.     out dx,    al        ; Switch back to page 0 registers
  765.     setport    EN0_BOUNDARY    ;Get boundary page
  766.     pause_
  767.     in al,    dx        ; ..
  768.     inc    al        ; Step boundary from last used page
  769.     cmp al,    sm_rstop_ptr    ; Wrap if needed
  770.     jne    rx_nwrap3    ; Go if not
  771.     mov al,    SM_RSTART_PG    ; Wrap to first RX page
  772. rx_nwrap3:
  773.     cmp al,    ah        ; Read all the frames?
  774.     je    recv_frame_break    ; Finished them all
  775.  
  776.     mov    ah,al        ; make a byte address. E.G. page
  777.     mov    al,0        ; 46h becomes 4600h into buffer
  778.     mov    bl,ah
  779.     mov    cx,RCV_HDR_SIZE
  780.     mov    di,offset rcv_hdr
  781.     push    ds
  782.     pop    es        ; set es to right place
  783.     call    block_input
  784.     mov al,    rcv_hdr+EN_RBUF_STAT    ; Get the buffer status byte
  785.     test al,ENRSR_RXOK    ; Good frame?
  786.     jz    recv_err_no_rcv
  787.     call    rcv_frm        ; Yes, go accept it
  788.     jmp    recv_no_rcv
  789. recv_err_no_rcv:
  790.     or    byte ptr soft_rx_err_bits,al
  791.     add    word ptr soft_rx_errors,1
  792.     adc    word ptr soft_rx_errors+2,0
  793. recv_no_rcv:
  794.     mov al,    rcv_hdr+EN_RBUF_NXT_PG    ; Start of next frame
  795.     dec    al        ; Make previous page for new boundary
  796.     cmp al,    SM_RSTART_PG    ; Wrap around the bottom?
  797.     jge    rcv_nwrap4
  798.     mov al,    sm_rstop_ptr    ; Yes
  799.     dec al
  800. rcv_nwrap4:
  801.     loadport        ; Point at the Boundary Reg again
  802.      setport    EN0_BOUNDARY    ; ..
  803.     pause_
  804.     out dx,    al        ; Set new boundary
  805.     jmp    recv_frame    ; See if any more frames
  806.  
  807. recv_frame_break:
  808.     loadport        ; Point at Command register
  809.      setport    EN_CCMD        ; ..
  810.     pause_
  811.     mov al,    ENC_NODMA+ENC_PAGE0+ENC_START
  812.     out    dx,al
  813.     jmp    check_isr    ; See if any other interrupts pending
  814.  
  815. recv_no_frame:                ; Handle transmit flags.
  816.     test al,ENISR_TX+ENISR_TX_ERR    ; Frame transmitted?
  817.     jnz    isr_tx        ; Go if so.
  818.     jmp    isr_no_tx    ; Go if not.
  819. isr_tx:
  820.     mov ah,    al        ; Hold interrupt status bits
  821.     loadport        ; Point at Transmit Status Reg
  822.      setport    EN0_TSR        ; ..
  823.     pause_
  824.     in al,    dx        ; ..
  825.     test ah,ENISR_TX    ; Non-error TX?
  826.     jz    isr_tx_err    ; No, do TX error completion
  827.     call    count_soft_err    ; soft error ??
  828.     test al,ENTSR_COLL16    ; Jammed for 16 transmit tries?
  829.     jz    isr_tx_njam    ; Go if not
  830.     call    count_out_err    ; Yes, count those
  831. isr_tx_njam:
  832.     setport    EN0_ISR        ; Clear the TX complete flag
  833.     pause_
  834.     mov al,    ENISR_TX    ; ..
  835.     out dx,    al        ; ..
  836.     jmp    isr_tx_done
  837. isr_tx_err:
  838.     test al,ENTSR_FU    ; FIFO Underrun?
  839.     jz    isr_txerr_nfu
  840.     call    count_out_err    ; Yes, count those
  841. isr_txerr_nfu:
  842.     loadport        ; Clear the TX error completion flag
  843.     setport    EN0_ISR        ; ..
  844.     pause_
  845.     mov al,    ENISR_TX_ERR    ; ..
  846.     out dx,    al        ; ..
  847. isr_tx_done:
  848. ; If TX queue and/or TX shared memory ring buffer were being
  849. ; used, logic to step through them would go here.  However,
  850. ; in this version, we just clear the flags for background to notice.
  851.  
  852.      jmp    check_isr    ; See if any other interrupts on
  853.  
  854. isr_no_tx:
  855. ; Now check to see if any counters are getting full
  856.     test al,ENISR_COUNTERS    ; Interrupt to handle counters?
  857.     jnz    isr_stat    ; Go if so.
  858.     jmp    isr_no_stat    ; Go if not.
  859. isr_stat:
  860. ; We have to read the counters to clear them and to clear the interrupt.
  861. ; Version 1 of the PC/FTP driver spec doesn't give us
  862. ; anything useful to do with the data, though.
  863. ; Fix this up for V2 one of these days.
  864.     loadport        ; Point at first counter
  865.      setport    EN0_COUNTER0    ; ..
  866.     pause_
  867.     in al,    dx        ; Read the count, ignore it.
  868.     setport    EN0_COUNTER1
  869.     pause_
  870.     in al,    dx        ; Read the count, ignore it.
  871.     setport    EN0_COUNTER2
  872.     pause_
  873.     in al,    dx        ; Read the count, ignore it.
  874.     setport    EN0_ISR        ; Clear the statistics completion flag
  875.     pause_
  876.     mov al,    ENISR_COUNTERS    ; ..
  877.     out dx,    al        ; ..
  878. isr_no_stat:
  879.      jmp    check_isr    ; Anything else to do?
  880.  
  881. interrupt_done:
  882.     ret
  883.  
  884. ; Do the work of copying out a receive frame.
  885. ; Called with bl/ the page number of the frame header in shared memory
  886.  
  887.     public    rcv_frm
  888. rcv_frm:
  889.     mkle LE_RCVFRM_E, 0, 0, 0
  890.  
  891. ; first do a software multicast filter.
  892.     push    bx            ; save page.
  893.     cmp    mcast_sw_filter,1    ; do software check of mcast ?
  894.     jnz    rcv_frm_ok        ; no, accept.
  895.     mov    ax,word ptr rcv_hdr+EN_RBUF_NHDR ; get first word of address
  896.     test al,1            ; odd first byte
  897.     jz    rcv_frm_ok        ; must be our address if even
  898.     inc    word ptr mcast_sw_fin
  899.  
  900.     mov    bx,word ptr rcv_hdr+EN_RBUF_NHDR+2 ; get second word of address
  901.     mov    dx,word ptr rcv_hdr+EN_RBUF_NHDR+4 ; get third word of address
  902.  
  903.     mov    di,offset mcast_tab_b    ; point to table and broadcast
  904.     mov    cx,mcast_hcount        ; get number in table
  905.     inc    cx            ; plus the broadcast
  906. rcv_loop:
  907.     mov    si,di            ; save this table entry
  908.     cmp    ax,[di]
  909.     jnz    rcv_trynext
  910.     inc    di
  911.     inc    di
  912.     cmp    bx,[di]
  913.     jnz    rcv_trynext
  914.     inc    di
  915.     inc    di
  916.     cmp    dx,[di]
  917.     jz    rcv_mc_ok        ; got it.
  918. rcv_trynext:
  919.     mov    di,si            ; get table back,
  920.     add    di,6
  921.     loop    rcv_loop
  922.     pop    bx            ; restore bx
  923.     jmp    rcv_no_copy
  924.  
  925. rcv_mc_ok:
  926.     inc    word ptr mcast_sw_fout
  927. rcv_frm_ok:
  928. ; Set cx to length of this frame.
  929.     mov ch,    rcv_hdr+EN_RBUF_SIZE_HI    ; Extract size of frame
  930.     mov cl,    rcv_hdr+EN_RBUF_SIZE_LO    ; Extract size of frame
  931.     sub cx,    EN_RBUF_NHDR        ; Less the header stuff
  932. ; Set es:di to point to Ethernet type field.
  933.     mov di,    offset rcv_hdr+EN_RBUF_NHDR+EADDR_LEN+EADDR_LEN
  934.     push    cx            ; Save frame size
  935.     push    es
  936.     mov ax,    cs            ; Set ds = code
  937.     mov ds,    ax
  938.     mov es,ax
  939.     assume    ds:code
  940.  
  941.     mov    dl, BLUEBOOK        ;assume bluebook Ethernet.
  942.     mov    ax, es:[di]
  943.     xchg    ah, al
  944.     cmp     ax, 1500
  945.     ja    BlueBookPacket
  946.     inc    di            ;set di to 802.2 header
  947.     inc    di
  948.     mov    dl, IEEE8023
  949. BlueBookPacket:
  950.  
  951.     call    recv_find        ; See if type and size are wanted
  952.     pop    ds            ; RX page pointer in ds now
  953.     assume    ds:nothing
  954.     pop    cx
  955.     pop    bx
  956.     cld            ; Copies below are forward, please
  957.     mov ax,    es        ; Did recv_find give us a null pointer?
  958.     or ax,    di        ; ..
  959.     je    rcv_no_copy    ; If null, don't copy the data
  960.  
  961.     push    cx        ; We will want the count and pointer
  962.     push    es        ;  to hand to client after copying,
  963.     push    di        ;  so save them at this point
  964.     mov    ah,bl        ; set ax to page to start from
  965.     mov    al,EN_RBUF_NHDR    ; skip the header stuff
  966.     call    block_input
  967.     pop    si        ; Recover pointer to destination
  968.     pop    ds        ; Tell client it's his source
  969.     pop    cx        ; And it's this long
  970.     assume    ds:nothing
  971.     call    recv_copy    ; Give it to him
  972. rcv_no_copy:
  973.     push    cs        ; Put ds back in code space
  974.     pop    ds        ; ..
  975.     assume    ds:code
  976.     mkle LE_RCVFRM_X, 0, 0, 0
  977.     ret            ; That's it for rcv_frm
  978.  
  979.  
  980.     public    recv_exiting
  981. recv_exiting:
  982. ;called from the recv isr after interrupts have been acknowledged.
  983. ;Only ds and ax have been saved.
  984.     assume    ds:nothing
  985.     push    dx
  986.     loadport
  987.     setport    EN0_IMR        ; Tell card it can cause these interrupts
  988.     pause_
  989.     mov al,    ENISR_ALL
  990.     out dx,    al
  991.     pop    dx
  992.     ret
  993.  
  994.     include    timeout.asm
  995.  
  996. ;any code after this will not be kept after initialization.
  997. end_resident    label    byte
  998.  
  999. using_186_msg    db    "Using 80[123]86 I/O instructions.",CR,LF,'$'
  1000.  
  1001. ;standard EN0_DCFG contents:
  1002. endcfg    db    048h            ; Set burst mode, 8 deep FIFO
  1003.  
  1004. cfg_error:
  1005.     mov    dx,offset cfg_err_msg
  1006. error:
  1007.     mov    ah,9        ; Type the msg
  1008.     int    21h
  1009.     stc            ; Indicate error
  1010.     ret            ; Return to common code
  1011.  
  1012. ; Called once to initialize the NE2000 card
  1013.  
  1014.     public    etopen
  1015. etopen:                ; Initialize interface
  1016.  
  1017. ;Determine the processor type.  The 8088 and 8086 will actually shift ax
  1018. ;over by 33 bits, while the 80[123]86 use a shift count mod 32.
  1019. ;This bit lifted from NI5010 driver.
  1020.  
  1021.     mov    cl,33
  1022.     mov    ax,0ffffh
  1023.     shl    ax,cl
  1024.     jz    not_186
  1025.     mov    is_186,1
  1026.     mov    dx,offset using_186_msg
  1027.     mov    ah,9
  1028.     int    21h
  1029. not_186:
  1030.  
  1031. ;Step 1. Reset and stop the 8390.
  1032.  
  1033.     call    reset_board
  1034.  
  1035. ;Step 2. Init the Data Config Reg.
  1036.  
  1037.     loadport
  1038.     mov    al,endcfg
  1039.     setport    EN0_DCFG
  1040.     pause_
  1041.     out    dx,al
  1042.  
  1043. ;Step 3. Clear Remote Byte Count Regs.
  1044.  
  1045.     mov    al, 0
  1046.     setport    EN0_RCNTLO
  1047.     pause_
  1048.     out    dx,al
  1049.     setport    EN0_RCNTHI
  1050.     pause_
  1051.     out    dx,al
  1052.  
  1053. ;Step 4. Set receiver to monitor mode
  1054.  
  1055.     mov    al, ENRXCR_MON
  1056.     setport    EN0_RXCR
  1057.     pause_
  1058.     out    dx,al
  1059.  
  1060. ;Step 5. Place NIC into Loopback Mode 1.
  1061.  
  1062.     mov    al,ENTXCR_LOOP
  1063.     setport    EN0_TXCR
  1064.     pause_
  1065.     out    dx,al
  1066.  
  1067. ;Step 6. Do anything special that the card needs.
  1068.  
  1069.     call    init_card
  1070.  
  1071. ;Step 7. Re-init endcfg in case they put it into word mode.
  1072.  
  1073.     loadport
  1074.     mov    al,endcfg
  1075.     setport    EN0_DCFG
  1076.     pause_
  1077.     out    dx,al
  1078.  
  1079. ;Step 8. Init EN0_STARTPG to same value as EN0_BOUNDARY
  1080.  
  1081.     loadport
  1082.     mov    al,SM_RSTART_PG
  1083.     setport    EN0_STARTPG
  1084.     pause_
  1085.     out    dx,al
  1086.     mov    al,SM_RSTART_PG
  1087.     setport    EN0_BOUNDARY
  1088.     pause_
  1089.     out    dx,al
  1090.     mov    al,sm_rstop_ptr
  1091.     setport    EN0_STOPPG
  1092.     pause_
  1093.     out    dx,al
  1094.  
  1095. ;Step 9. Write 1's to all bits of EN0_ISR to clear pending interrupts.
  1096.  
  1097.     mov    al, 0ffh
  1098.     setport    EN0_ISR
  1099.     pause_
  1100.     out    dx,al
  1101.  
  1102. ;Step 10. Init EN0_IMR as desired.
  1103.  
  1104.     mov    al, ENISR_ALL
  1105.     setport    EN0_IMR
  1106.     pause_
  1107.     out    dx,al
  1108.  
  1109. ;Step 11. Init the Ethernet address and multicast filters.
  1110.  
  1111.     call    set_8390_eaddr  ; Now set the address in the 8390 chip
  1112.     call    set_8390_multi  ; Put the right stuff into 8390's multicast masks
  1113.  
  1114. ;Step 12. Program EN_CCMD for page 1.
  1115.  
  1116.     loadport
  1117.     mov    al, ENC_PAGE1 + ENC_NODMA + ENC_STOP
  1118.     setport    EN_CCMD
  1119.     pause_
  1120.     out    dx,al
  1121.  
  1122. ;Step 13. Program the Current Page Register to same value as Boundary Pointer.
  1123.  
  1124.     mov    al,SM_RSTART_PG
  1125.     setport    EN1_CURPAG
  1126.     pause_
  1127.     out    dx,al
  1128.  
  1129. ;Step 14. Program EN_CCMD back to page 0, and start it.
  1130.  
  1131.     mov    al, ENC_NODMA + ENC_START
  1132.     setport    EN_CCMD
  1133.     pause_
  1134.     out    dx,al
  1135.  
  1136.     mov    al, 0            ;set transmitter mode to normal.
  1137.     setport    EN0_TXCR
  1138.     pause_
  1139.     out    dx,al
  1140.  
  1141.     call    set_recv_isr    ; Put ourselves in interrupt chain
  1142.  
  1143.     loadport
  1144.     setport    EN0_RXCR    ; Tell it what frames to accept
  1145.     pause_
  1146.     mov al,    rxcr_bits       ; As most recently set by set_mode
  1147.     out dx,    al
  1148.  
  1149.     ram_enable
  1150.  
  1151.     mov    al, int_no        ; Get board's interrupt vector
  1152.     add    al, 8
  1153.     cmp    al, 8+8            ; Is it a slave 8259 interrupt?
  1154.     jb    set_int_num        ; No.
  1155.     add    al, 70h - 8 - 8        ; Map it to the real interrupt.
  1156. set_int_num:
  1157.     xor    ah, ah            ; Clear high byte
  1158.     mov    int_num, ax        ; Set parameter_list int num.
  1159.  
  1160.     mov dx,    offset end_resident    ; Report our size
  1161.     clc                ; Say no error
  1162.     ret                ; Back to common code
  1163.  
  1164.